﻿using System;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using CDude.Tools;

using Sce.Psm;

namespace CDude.Vita
{
    public partial class VCg_MainForm : Form
    {
        private object actionlock = new object();
        private PsmDeviceConnection connection = null;
        private ContextMenu contextmenu;
        private int deviceid = 0;
        private string install_inputpath = null;
        private string love = "";
        private DevicePlugNotifier notifier = null;
        private string pack_inputfolderpath = null;
        private bool pack_isappdir = false;
        private string pack_outfilepath = null;
        private OperationInProgressForm progressform = new OperationInProgressForm();

        public VCg_MainForm()
        {
            notifier = new DevicePlugNotifier(this);
            progressform.Owner = this;

            new Task(() =>
            {
                RandomText randomtext = GetRandomText();
                randomLabel.Text = randomtext.Text;
                randomLabel.Font = new Font(randomLabel.Font.SystemFontName, (float)randomtext.FontSize);
            }).Start();

            InitializeComponent();

            MenuItem menu_restore = new MenuItem("Restore") { Enabled = false };
            MenuItem menu_move = new MenuItem("Move") { Enabled = false };
            MenuItem menu_size = new MenuItem("Size") { Enabled = false };
            MenuItem menu_minimize = new MenuItem("Minimize", minimizeButton_Click);
            MenuItem menu_maximize = new MenuItem("Maximize") { Enabled = false };
            MenuItem menu_seperator = new MenuItem("-");
            MenuItem menu_close = new MenuItem("Close", closeButton_Click, Shortcut.AltF4) { DefaultItem = true };
            contextmenu = new ContextMenu(new MenuItem[] { menu_restore, menu_move, menu_size, menu_minimize, menu_maximize, menu_seperator, menu_close });

            titleBarText.Parent = titleBar;

            Bitmap smallicon = Icon.ExtractAssociatedIcon(System.Windows.Forms.Application.ExecutablePath).ToBitmap();
            titleBarIcon.Image = smallicon;

            versionLabel.Text = Assembly.GetExecutingAssembly().GetName().Version.ToString();

            notifier.DeviceChanged += onDeviceChanged;
            notifier.NoDataDeviceChanged += onNoDataDeviceChanged;
            updateDeviceList(true);
        }

        private PsmDevice[] devices { get { return PsmDevice.ListDevices(); } }

        #region Device List

        private void connectButton_Click(object sender, EventArgs e)
        {
            deviceid = deviceList.SelectedIndex;

            connectButton.Enabled = false;
            toggleDeviceList();

            setStatusText("Connecting to device {0}...", deviceid.ToString());
            if (devices.Length == 0 || deviceid > devices.Length || !devices[deviceid].Online)
            {
                setStatusText("Device {0} is offline! Please start the PS Mobile Dev Assistant on the device and make sure the device is not asleep.", deviceid.ToString());
                toggleDeviceList();
                updateDeviceList(false);
                return;
            }

            try
            {
                connection = devices[deviceid].Connect();
                setStatusText("Connected to device {0}!", deviceid.ToString());
                toggleActions();
                toggleDisconnectButtons();
            }
            catch
            {
                setStatusText("Could not connect to device {0}!", deviceid.ToString());
                toggleDeviceList();
                connectButton.Enabled = true;
                updateDeviceList(false);
            }
        }

        private void deviceList_SelectedIndexChanged(object sender, EventArgs e)
        {
            onChangedIndex();
        }

        private void disconnect(bool showstatus)
        {
            toggleDisconnectButtons();
            try
            {
                connection.Disconnect();
            }
            catch { }
            finally
            {
                toggleDeviceList();
                toggleActions();
                if (showstatus) setStatusText("Disconnected!");
                updateDeviceList(false);
                if (devices.Length > 0 && devices.Length >= deviceid && devices[deviceList.SelectedIndex].Online)
                    connectButton.Enabled = true;
            }
        }

        private void disconnectButton_Click(object sender, EventArgs e)
        {
            disconnect(true);
        }

        private void killButton_Click(object sender, EventArgs e)
        {
            toggleDisconnectButtons();
            try
            {
                connection.KillRunningApps();
            }
            catch { }
            finally
            {
                toggleDeviceList();
                toggleActions();
                setStatusText("Killed PSM app!");
                updateDeviceList(false);
            }
        }

        private void onChangedIndex()
        {
            if (deviceList.SelectedIndex != -1 && devices.Length > 0 && devices.Length >= deviceid && devices[deviceList.SelectedIndex].Online)
                connectButton.Enabled = true;
            else
                connectButton.Enabled = false;
        }

        private void onDeviceChanged(object sender, DevicePlugEventArgs e)
        {
            if (connection != null && (!e.Added || !connection.IsValid))
            {
                disconnect(true);
                updateDeviceList(false);
            }
            else if (connection == null || !connection.IsValid)
                updateDeviceList(true);
        }

        private void onNoDataDeviceChanged(object sender, EventArgs e)
        {
            Thread.Sleep(500);
            if (connection != null && (devices.Length > 0 && deviceid <= devices.Length && !devices[deviceid].Online || !connection.IsValid))
            {
                disconnect(true);
                updateDeviceList(false);
            }
            else if (connection == null)
            {
                connectButton.Enabled = false;
                updateDeviceList(true);
            }
        }

        private void refreshButton_Click(object sender, EventArgs e)
        {
            updateDeviceList(true);
        }

        private void updateDeviceList(bool showstatus)
        {
            int previousselection = deviceList.SelectedIndex;
            if (showstatus)
                setStatusText("Getting device list...");

            deviceList.SelectedItem = null;
            deviceList.Items.Clear();
            for (int i = 0; i < devices.Length; i++)
            {
                string onlinetext = "online";
                if (!devices[i].Online) onlinetext = "offline";
                deviceList.Items.Add("Device #" + i + ": " + devices[i].Type + " \"" + devices[i].Name + "\" " + devices[i].Guid + " (" + onlinetext + ")");
            }

            if (devices.Length == 0)
            {
                if (showstatus)
                    setStatusText("No devices connected!");
            }
            else
            {
                if (deviceList.SelectedIndex == -1)
                {
                    if (deviceList.Items.Count > 0)
                        deviceList.SelectedIndex = 0;
                }
                else
                    deviceList.SelectedIndex = previousselection;

                if (showstatus)
                    setStatusText("Got device list!");
            }
        }

        #endregion Device List

        #region Actions

        #region Install

        private void install()
        {
            if (breakOut()) return;
            if (containsNull(install_inputPackagePath.Text, install_desiredAppName.Text)) return;
            lock (actionlock)
            {
                toggleProgressForm();
                Task installertask = new Task(() =>
                {
                    connection.InstallApp(install_inputpath, install_desiredAppName.Text);
                    setStatusText("Installed {0}! You may need to restart the development assistant for changes to show up.", install_desiredAppName.Text);
                    toggleProgressForm();
                });
                setStatusText("Installing {0}...", install_desiredAppName.Text);
                installertask.Start();
                new Task(() => { verifyWorkingDevices(ref installertask); }).Start();
            }
        }

        private void install_desiredAppName_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter && install_DoButton.Enabled)
                install();
        }

        private void install_DoButton_Click(object sender, EventArgs e)
        {
            install();
        }

        private void install_selectPackageButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog o = new OpenFileDialog();
            o.Filter = "PS Mobile Packages|*.psspac";
            o.Title = "Select Package";
            DialogResult r = o.ShowDialog();

            if (r == DialogResult.OK)
            {
                install_inputpath = o.FileName;
                install_inputPackagePath.Text = o.SafeFileName;
            }
        }

        #endregion Install

        #region Launch

        private void launch()
        {
            if (breakOut()) return;
            if (containsNull(launch_appName.Text, launch_argsBox.Text)) return;
            appOutputBox.Clear();
            lock (actionlock)
            {
                toggleProgressForm();
                Task launchertask = new Task(() =>
                {
                    connection.LaunchApp(launch_appName.Text, launch_debugCheck.Checked, launch_profileCheck.Checked, launch_keepnetCheck.Checked, launch_argsBox.Text);
                    if (launch_displayOutputCheck.Checked)
                        PsmDevice.SetConsoleCallback(new PsmDeviceConsoleCallback(new TextBoxWriter(appOutputBox).WriteLine));
                    else
                        PsmDevice.SetConsoleCallback(null);
                    setStatusText("Launched {0}!", launch_appName.Text);
                    toggleProgressForm();
                    Thread.Sleep(250);
                    if (!launch_keepnetCheck.Checked) disconnect(false);
                });
                setStatusText("Launching {0}...", launch_appName.Text);
                launchertask.Start();
                new Task(() => { verifyWorkingDevices(ref launchertask); }).Start();
            }
        }

        private void launch_appName_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter && launch_DoButton.Enabled)
                launch();
        }

        private void launch_argsBox_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter && launch_DoButton.Enabled)
                launch();
        }

        private void launch_displayOutputCheck_CheckedChanged(object sender, EventArgs e)
        {
            if (launch_displayOutputCheck.Checked)
                launch_keepnetCheck.Checked = true;

            launch_keepnetCheck.Enabled = !launch_keepnetCheck.Enabled;
        }

        private void launch_DoButton_Click(object sender, EventArgs e)
        {
            launch();
        }

        #endregion Launch

        #region Pack

        private void pack()
        {
            if (containsNull(pack_inputFolderPath.Text, pack_outputPackagePath.Text)) return;
            string suffix = null;

            if (!pack_isappdir)
                suffix = "\\Application";

            lock (actionlock)
            {
                toggleProgressForm();
                Task packertask = new Task(() =>
                {
                    PsmDevice.CreatePackage(pack_outfilepath, pack_inputfolderpath + suffix);
                    setStatusText("Packed {0}!", pack_outputPackagePath.Text);
                    toggleProgressForm();
                });
                setStatusText("Packing {0}...", pack_outputPackagePath.Text);
                packertask.Start();
            }
        }

        private void pack_DoButton_Click(object sender, EventArgs e)
        {
            Environment.Exit(0);
            pack();
        }

        private void pack_selectFolderButton_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog folderdialog = new FolderBrowserDialog();
            folderdialog.Description = "Select the folder containing the app:";
            folderdialog.ShowNewFolderButton = false;
            DialogResult result = folderdialog.ShowDialog();

            if (result == DialogResult.OK)
            {
                bool hasexe = true;

                if (!File.Exists(folderdialog.SelectedPath + "\\pssapp.exe") && !File.Exists(folderdialog.SelectedPath + "\\Application\\pssapp.exe"))
                {
                    DialogResult exeresult = MessageBox.Show("No executable was found. Continue?", "Warning – VitaCOMgui", MessageBoxButtons.YesNo);
                    if (exeresult == DialogResult.Yes)
                    {
                        hasexe = false;
                        pack_isappdir = true;
                    }
                    else
                    {
                        pack_isappdir = false;
                        return;
                    }
                }

                pack_isappdir = hasexe && File.Exists(folderdialog.SelectedPath + "\\pssapp.exe");

                pack_inputfolderpath = folderdialog.SelectedPath;
                pack_inputFolderPath.Text = folderdialog.SelectedPath.Replace("/", "\\").Substring(folderdialog.SelectedPath.LastIndexOf("\\") + 1);
            }
        }

        private void pack_selectOutputButton_Click(object sender, EventArgs e)
        {
            SaveFileDialog s = new SaveFileDialog();
            s.Filter = "PS Mobile Packages|*.psspac";
            s.Title = "Type Output File";
            DialogResult r = s.ShowDialog();

            if (r == DialogResult.OK)
            {
                pack_outfilepath = s.FileName;
                pack_outputPackagePath.Text = Path.GetFileName(s.FileName);
            }
        }

        #endregion Pack

        #region Uninstall

        private void uninstall()
        {
            if (breakOut()) return;
            if (containsNull(uninstall_appName.Text)) return;

            lock (actionlock)
            {
                toggleProgressForm();
                Task uninstallertask = new Task(() =>
                {
                    connection.UninstallApp(uninstall_appName.Text);
                    setStatusText("Uninstalled {0}! You may need to restart the development assistant for changes to show up.", uninstall_appName.Text);
                    toggleProgressForm();
                });
                setStatusText("Unistalling {0}...", uninstall_appName.Text);
                uninstallertask.Start();
                new Task(() => { verifyWorkingDevices(ref uninstallertask); }).Start();
            }
        }

        private void uninstall_appName_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter && uninstall_DoButton.Enabled)
                uninstall();
        }

        private void uninstall_DoButton_Click(object sender, EventArgs e)
        {
            uninstall();
        }

        #endregion Uninstall

        #endregion Actions

        #region Misc

        protected override unsafe void WndProc(ref Message m)
        {
            base.WndProc(ref m);
            if (notifier != null)
                notifier.WndProc(ref m);
        }

        private static RandomText GetRandomText()
        {
            try
            {
                return new RandomText("http://data.celcodioc.host22.com/vitacom/randomtextv2");
            }
            catch
            {
                return new RandomText(8, "Could not download random text!");
            }
        }

        private void appOutputBox_KeyDown(object sender, KeyEventArgs e)
        {
            love += (char)e.KeyValue;
            if (love.IndexOf("I LOVE VITACOM") != -1)
            {
                MessageBox.Show("I love you too! <3");
                love = "";
            }
        }

        private bool breakOut()
        {
            if (devices.Length == 0 || deviceid > devices.Length || !devices[deviceid].Online)
            {
                toggleActions();
                toggleDisconnectButtons();
                setStatusText("Lost connection with device {0}!", deviceid.ToString());
                updateDeviceList(false);
                toggleDeviceList();
                return true;
            }

            return false;
        }

        private bool containsNull(params object[] obj)
        {
            for (int i = 0; i < obj.Length; i++)
                if (obj[i] == null) return true;
            return false;
        }

        private void setStatusText(params object[] text)
        {
            string o = text[0].ToString();
            for (int i = 1; i < text.Length; i++)
                o = o.Replace("{" + (i - 1) + "}", text[i].ToString());
            statusText.Text = o;
        }

        private void toggleActions()
        {
            install_DoButton.Enabled = !install_DoButton.Enabled;
            uninstall_DoButton.Enabled = !uninstall_DoButton.Enabled;
            launch_DoButton.Enabled = !launch_DoButton.Enabled;
        }

        private void toggleDeviceList()
        {
            refreshButton.Enabled = !refreshButton.Enabled;
            deviceList.Enabled = !deviceList.Enabled;
        }

        private void toggleDisconnectButtons()
        {
            disconnectButton.Enabled = !disconnectButton.Enabled;
            killButton.Enabled = !killButton.Enabled;
        }

        private void toggleProgressForm()
        {
            this.Enabled = !this.Enabled;
            progressform.Visible = !progressform.Visible;
        }

        private void verifyWorkingDevices(ref Task tasktomonitor)
        {
            while (devices.Length > 0 && deviceid <= devices.Length && connection.IsValid && devices[deviceid].Online)
            {
                if (tasktomonitor.IsCompleted)
                    break;
                Thread.Sleep(250);
            }
            Thread.Sleep(150);
            if (!this.Enabled)
            {
                toggleProgressForm();
                disconnect(true);
                setStatusText("The operation did not complete successfully!");
            }
        }

        public class RandomText
        {
            public double FontSize;
            public string Text;

            public RandomText(double fontsize, string text)
            {
                this.FontSize = fontsize;
                this.Text = text;
            }

            public RandomText(string url)
            {
                XmlTextReader xmlreader = new XmlTextReader(url);
                string nodename = null;
                while (xmlreader.Read())
                {
                    switch (xmlreader.NodeType)
                    {
                        case XmlNodeType.Element:
                            nodename = xmlreader.Name;
                            break;
                        case XmlNodeType.Text:
                            switch (nodename)
                            {
                                case "FontSize":
                                    this.FontSize = Convert.ToDouble(xmlreader.Value);
                                    break;
                                case "Text":
                                    this.Text = xmlreader.Value;
                                    break;
                            }
                            break;
                    }
                }
            }
        }

        #endregion Misc

        #region Ugly Interface

        private void closeButton_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void minimizeButton_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Minimized;
        }

        private void titleBar_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
                contextmenu.Show(this, new Point(e.X, e.Y));
        }

        private void titleBarIcon_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                contextmenu.Show(this, new Point(titleBarIcon.Location.X, titleBarIcon.Location.Y + titleBarIcon.Size.Height));
        }

        private void titleBarText_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                NativeMethods.ReleaseCapture();
                NativeMethods.SendMessage(this.Handle, 0xA1, (IntPtr)2, (IntPtr)0);
            }
        }

        #endregion Ugly Interface
    }
}